iOS HTTPS迁移之路-3. 了解SPDY协议

SPDY 即“Speedy”发音,有速度之意,从名字便可知谷歌当年提出的这个方案的目标就是为了解决 HTTP 的性能瓶颈,缩短 web 页面的 load 时间。SPDY 是一个二进制协议,并不像 HTTP 那样直观易读。

SPDY 的设计

SPDY 解决的问题

  • 多路复用:这名字听起来很高大上。其实理解起来,就是在一个 TCP 连接中,分出了多个 SPDY Stream 流通道,每个通道是并行的,不用相互等待,且各自负责一个请求和响应。这无疑会降低延迟和提升带宽的使用率。之前 HTTP/1.1 就面临着 head-of-line blocking 队首阻塞问题,也就是一个 response 响应如果迟迟没有回来,会阻塞住后续的 response 响应。后面我们用 WWDC 的一些截图来说明问题。
  • 请求优先级:SPDY 允许为通道中的请求设置优先级,使得高优先级的请求能够优先得到回复响应。
  • 请求头部压缩:我们知道,HTTP 协议是无状态的,无法对之前的请求和响应状态进行管理,而 Cookie 的引入正是为了解决这个问题。服务端通过客户端发送的 Cookie 来知晓是哪个客户端发送的 HTTP 请求,从而进行后续的操作。在 HTTP 请求的头部,还有一个 User Agent,这个字段携带着客户端的一些信息,如 iOS/Android 系统版本等信息。显而易见的是,Cookie 以及 User Agent 在每次的请求中会被携带进去,会耗费一定的流量。SPDY能够对 HTTP 请求头部进行压缩,会一定程度上节省不少流量。
  • 服务器推送:HTTP 请求一般由客户端主动发送,服务端进行响应。而 SPDY 可以让服务端将客户端需要的内容 push 过去,不需要被动的等待客户端发送请求。有点像我们目前的 socket 连接下的服务器推送。

关于多路复用

我们使用【WWDC2014 707 Session whats_new_in_foundation_networking】中的几张图来说明下问题:

图1: HTTP/1.1 未使用 pipelining


图2: HTTP/1.1 使用 pipelining


图3: SPDY 多路复用
  • 图1: HTTP/1.1 未使用 pipelining

    style.cssdata.xml的请求必须在收到image.jpg的 response 之后才能依次发送出去,可想而知,这种方式会造成高延迟和低带宽使用率。

  • 图2: HTTP/1.1 使用 pipelining

    style.cssdata.xml的请求不必等到image.jpg响应回来之后才进行发送,而是在image.jpg请求发出去之后就依次发送了出去,而style.cssdata.xml的响应(图中深色部分)必须要在image.jpg的响应之后。如果image.jpg请求的响应数据量比较大(图片较大),加之网络延迟严重等因素,会造成style.cssdata.xml的响应迟迟不能回来,而对于网页渲染而言,资源的优先级却是data.xml为最高优先级,style.css资源为次优先级,image.jpg为最低,最高优先级的资源却被最低优先级的资源请求影响,也就是受到了image.jpg响应的阻塞,即head-of-line blocking队首阻塞。

  • 图3: SPDY 多路复用

    我们从图中看到,SPDY的多路复用,从请求的发送时序上和HTTP/1.1使用pipelining是一样的,关键不同的地方在于响应返回时序上。在这个 TCP 连接中,分出了3个SPDY Stream流通道,即imagestyle.css, data.xml3个资源对应的流通道,每个通道各自负责一个请求和响应,并行且不用相互等待。

我该使用 SPDY 么?

  • 更好的用户体验
    • 对于长连接的延迟的减少可以明显改善用户体验。
    • 苹果发现:在某些情况下,SPDY速度要比 HTTP/1.1 快25%。
  • 额外的获益
    • 减少 CPU 的使用率(一次SSL握手过程取代多次)
    • 相同的服务器架构支持更多的客户端连接

SPDY 并不是总优于 HTTP/1.1:

  1. HTTP/1.1使用多个并行TCP连接要比SPDY的单个TCP连接要快。
  2. 许多SPDY的实现因为 CRIME 漏洞的影响禁用了头部压缩,所以头部压缩并不能带来很大的收益。

iOS8启用SPDY

苹果于 iOS8 和 OSX Yosemite 后支持了SPDY,NSURLSession默认就支持了SPDY,支持的版本范围有 SPDY/2,SPDY/3,SPDY/3.1。

1
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
                              dataTaskWithURL:url
                              completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
                              
                              }];
[task resume];

将会默认启用SPDY,原来的代码不用做任何的改变。

坚持原创技术分享,您的支持将鼓励我继续创作!